home *** CD-ROM | disk | FTP | other *** search
/ Isometric Game Programming with DirectX 7.0 / Isometric Game Programming.iso / directx / dxf / samples / multimedia / directshow / baseclasses / strmctl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-02  |  12.6 KB  |  402 lines

  1. //------------------------------------------------------------------------------
  2. // File: StrmCtl.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) 1996 - 2000, Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include <streams.h>
  11. #include <strmctl.h>
  12.  
  13. CBaseStreamControl::CBaseStreamControl()
  14. : m_StreamState(STREAM_FLOWING)
  15. , m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
  16. , m_tStartTime(MAX_TIME)
  17. , m_tStopTime(MAX_TIME)
  18. , m_dwStartCookie(0)
  19. , m_dwStopCookie(0)
  20. , m_pRefClock(NULL)
  21. , m_FilterState(State_Stopped)
  22. , m_bIsFlushing(FALSE)
  23. , m_bStopSendExtra(FALSE)
  24. {}
  25.  
  26. CBaseStreamControl::~CBaseStreamControl()
  27. {
  28.     // Make sure we release the clock.
  29.     SetSyncSource(NULL);
  30.     return;
  31. }
  32.  
  33.  
  34. STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
  35. {
  36.     CAutoLock lck(&m_CritSec);
  37.     m_bStopSendExtra = FALSE;    // reset
  38.     m_bStopExtraSent = FALSE;
  39.     if (ptStop)
  40.     {
  41.         if (*ptStop == MAX_TIME)
  42.         {
  43.             DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
  44.             CancelStop();
  45.         // If there's now a command to start in the future, we assume
  46.         // they want to be stopped when the graph is first run
  47.         if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
  48.             m_StreamState = STREAM_DISCARDING;
  49.                 DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
  50.         }
  51.             return NOERROR;
  52.         }
  53.         DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
  54.                 (int)(*ptStop/10000), bSendExtra));
  55.     // if the first command is to stop in the future, then we assume they
  56.         // want to be started when the graph is first run
  57.     if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
  58.         m_StreamState = STREAM_FLOWING;
  59.             DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
  60.     }
  61.         m_bStopSendExtra = bSendExtra;
  62.         m_tStopTime = *ptStop;
  63.         m_dwStopCookie = dwCookie;
  64.         m_StreamStateOnStop = STREAM_DISCARDING;
  65.     }
  66.     else
  67.     {
  68.         DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
  69.     // sending an extra frame when told to stop now would mess people up
  70.         m_bStopSendExtra = FALSE;
  71.         m_tStopTime = MAX_TIME;
  72.         m_dwStopCookie = 0;
  73.         m_StreamState = STREAM_DISCARDING;
  74.         m_StreamStateOnStop = STREAM_FLOWING;    // no pending stop
  75.     }
  76.     // we might change our mind what to do with a sample we're blocking
  77.     m_StreamEvent.Set();
  78.     return NOERROR;
  79. }
  80.  
  81. STDMETHODIMP CBaseStreamControl::StartAt
  82. ( const REFERENCE_TIME *ptStart, DWORD dwCookie )
  83. {
  84.     CAutoLock lck(&m_CritSec);
  85.     if (ptStart)
  86.     {
  87.         if (*ptStart == MAX_TIME)
  88.         {
  89.             DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
  90.             CancelStart();
  91.         // If there's now a command to stop in the future, we assume
  92.         // they want to be started when the graph is first run
  93.         if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
  94.                 DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
  95.             m_StreamState = STREAM_FLOWING;
  96.         }
  97.             return NOERROR;
  98.         }
  99.         DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
  100.     // if the first command is to start in the future, then we assume they
  101.         // want to be stopped when the graph is first run
  102.     if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
  103.             DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
  104.         m_StreamState = STREAM_DISCARDING;
  105.     }
  106.         m_tStartTime = *ptStart;
  107.         m_dwStartCookie = dwCookie;
  108.         // if (m_tStopTime == m_tStartTime) CancelStop();
  109.     }
  110.     else
  111.     {
  112.         DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
  113.         m_tStartTime = MAX_TIME;
  114.         m_dwStartCookie = 0;
  115.         m_StreamState = STREAM_FLOWING;
  116.     }
  117.     // we might change our mind what to do with a sample we're blocking
  118.     m_StreamEvent.Set();
  119.     return NOERROR;
  120. }
  121.  
  122. //  Retrieve information about current settings
  123. STDMETHODIMP CBaseStreamControl::GetInfo(AM_STREAM_INFO *pInfo)
  124. {
  125.     if (pInfo == NULL)
  126.     return E_POINTER;
  127.  
  128.     pInfo->tStart = m_tStartTime;
  129.     pInfo->tStop  = m_tStopTime;
  130.     pInfo->dwStartCookie = m_dwStartCookie;
  131.     pInfo->dwStopCookie  = m_dwStopCookie;
  132.     pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
  133.     pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
  134.     pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
  135.     switch (m_StreamState) {
  136.     default:
  137.         DbgBreak("Invalid stream state");
  138.     case STREAM_FLOWING:
  139.         break;
  140.     case STREAM_DISCARDING:
  141.         pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
  142.         break;
  143.     }
  144.     return S_OK;
  145. }
  146.  
  147.  
  148. void CBaseStreamControl::ExecuteStop()
  149. {
  150.     ASSERT(CritCheckIn(&m_CritSec));
  151.     m_StreamState = m_StreamStateOnStop;
  152.     if (m_dwStopCookie && m_pSink) {
  153.     DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
  154.                             m_dwStopCookie));
  155.         m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
  156.     }
  157.     CancelStop(); // This will do the tidy up
  158. }
  159.  
  160. void CBaseStreamControl::ExecuteStart()
  161. {
  162.     ASSERT(CritCheckIn(&m_CritSec));
  163.     m_StreamState = STREAM_FLOWING;
  164.     if (m_dwStartCookie) {
  165.     DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
  166.                             m_dwStartCookie));
  167.         m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
  168.     }
  169.     CancelStart(); // This will do the tidy up
  170. }
  171.  
  172. void CBaseStreamControl::CancelStop()
  173. {
  174.     ASSERT(CritCheckIn(&m_CritSec));
  175.     m_tStopTime = MAX_TIME;
  176.     m_dwStopCookie = 0;
  177.     m_StreamStateOnStop = STREAM_FLOWING;
  178. }
  179.  
  180. void CBaseStreamControl::CancelStart()
  181. {
  182.     ASSERT(CritCheckIn(&m_CritSec));
  183.     m_tStartTime = MAX_TIME;
  184.     m_dwStartCookie = 0;
  185. }
  186.  
  187.  
  188. // This guy will return one of the three StreamControlState's.  Here's what the caller
  189. // should do for each one:
  190. //
  191. // STREAM_FLOWING:      Proceed as usual (render or pass the sample on)
  192. // STREAM_DISCARDING:   Calculate the time 'til *pSampleStart and wait that long
  193. //                      for the event handle (GetStreamEventHandle()).  If the
  194. //                      wait expires, throw the sample away.  If the event
  195. //            fires, call me back, I've changed my mind.
  196. //            I use pSampleStart (not Stop) so that live sources don't
  197. //             block for the duration of their samples, since the clock
  198. //            will always read approximately pSampleStart when called
  199.  
  200.  
  201. // All through this code, you'll notice the following rules:
  202. // - When start and stop time are the same, it's as if start was first
  203. // - An event is considered inside the sample when it's >= sample start time
  204. //   but < sample stop time
  205. // - if any part of the sample is supposed to be sent, we'll send the whole
  206. //   thing since we don't break it into smaller pieces
  207. // - If we skip over a start or stop without doing it, we still signal the event
  208. //   and reset ourselves in case somebody's waiting for the event, and to make
  209. //   sure we notice that the event is past and should be forgotten
  210. // Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
  211. //
  212. // 1.    xo<-->        start then stop
  213. // 2.    ox<-->        stop then start
  214. // 3.     x<o->        start
  215. // 4.     o<x->        stop then start
  216. // 5.     x<-->o        start
  217. // 6.     o<-->x        stop
  218. // 7.      <x->o        start
  219. // 8.      <o->x        no change
  220. // 9.      <xo>        start
  221. // 10.      <ox>        stop then start
  222. // 11.      <-->xo    no change
  223. // 12.      <-->ox    no change
  224. // 13.     x<-->        start
  225. // 14.    <x->        start
  226. // 15.    <-->x        no change
  227. // 16.   o<-->        stop
  228. // 17.      <o->        no change
  229. // 18.      <-->o        no change
  230. // 19.    <-->        no change
  231.  
  232.  
  233. enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
  234. ( const REFERENCE_TIME * pSampleStart, const REFERENCE_TIME * pSampleStop )
  235. {
  236.     CAutoLock lck(&m_CritSec);
  237.  
  238.     ASSERT(!m_bIsFlushing);
  239.     ASSERT(pSampleStart && pSampleStop);
  240.  
  241.     // Don't ask me how I came up with the code below to handle all 19 cases
  242.     // - DannyMi
  243.  
  244.     if (m_tStopTime >= *pSampleStart)
  245.     {
  246.         if (m_tStartTime >= *pSampleStop)
  247.         return m_StreamState;        // cases  8 11 12 15 17 18 19
  248.     if (m_tStopTime < m_tStartTime)
  249.         ExecuteStop();            // case 10
  250.     ExecuteStart();                         // cases 3 5 7 9 13 14
  251.     return m_StreamState;
  252.     }
  253.  
  254.     if (m_tStartTime >= *pSampleStop)
  255.     {
  256.         ExecuteStop();                          // cases 6 16
  257.         return m_StreamState;
  258.     }
  259.  
  260.     if (m_tStartTime <= m_tStopTime)
  261.     {
  262.     ExecuteStart();
  263.     ExecuteStop();
  264.         return m_StreamState;        // case 1
  265.     }
  266.     else
  267.     {
  268.     ExecuteStop();
  269.     ExecuteStart();
  270.         return m_StreamState;        // cases 2 4
  271.     }
  272. }
  273.  
  274.  
  275. enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
  276. {
  277.  
  278.     REFERENCE_TIME rtBufferStart, rtBufferStop;
  279.     const BOOL bNoBufferTimes =
  280.               pSample == NULL ||
  281.               FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
  282.  
  283.     StreamControlState state;
  284.     LONG lWait;
  285.  
  286.     do
  287.         {
  288.          // something has to break out of the blocking
  289.             if (m_bIsFlushing || m_FilterState == State_Stopped)
  290.         return STREAM_DISCARDING;
  291.  
  292.             if (bNoBufferTimes) {
  293.                 //  Can't do anything until we get a time stamp
  294.                 state = m_StreamState;
  295.                 break;
  296.             } else {
  297.                 state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
  298.                 if (state == STREAM_FLOWING)
  299.             break;
  300.  
  301.         // we aren't supposed to send this, but we've been
  302.         // told to send one more than we were supposed to
  303.         // (and the stop isn't still pending and we're streaming)
  304.         if (m_bStopSendExtra && !m_bStopExtraSent &&
  305.                     m_tStopTime == MAX_TIME &&
  306.                     m_FilterState != State_Stopped) {
  307.             m_bStopExtraSent = TRUE;
  308.             DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
  309.                                 m_dwStopCookie));
  310.             state = STREAM_FLOWING;
  311.             break;
  312.         }
  313.             }
  314.  
  315.             // We're in discarding mode
  316.  
  317.             // If we've no clock, discard as fast as we can
  318.             if (!m_pRefClock) {
  319.         break;
  320.  
  321.         // If we're paused, we can't discard in a timely manner because
  322.         // there's no such thing as stream times.  We must block until
  323.         // we run or stop, or we'll end up throwing the whole stream away
  324.         // as quickly as possible
  325.         } else if (m_FilterState == State_Paused) {
  326.         lWait = INFINITE;
  327.  
  328.         } else {
  329.             // wait until it's time for the sample until we say "discard"
  330.             // ("discard in a timely fashion")
  331.             REFERENCE_TIME rtNow;
  332.                 EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
  333.                 rtNow -= m_tRunStart;   // Into relative ref-time
  334.                 lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
  335.                 if (lWait < 10) break; // Not worth waiting - discard early
  336.         }
  337.  
  338.     } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
  339.  
  340.     return state;
  341. }
  342.  
  343.  
  344. void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
  345. {
  346.     CAutoLock lck(&m_CritSec);
  347.  
  348.     // or we will get confused
  349.     if (m_FilterState == new_state)
  350.     return;
  351.  
  352.     switch (new_state)
  353.     {
  354.         case State_Stopped:
  355.  
  356.             DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
  357.  
  358.         // execute any pending starts and stops in the right order,
  359.         // to make sure all notifications get sent, and we end up
  360.         // in the right state to begin next time (??? why not?)
  361.  
  362.         if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
  363.         ExecuteStart();
  364.         } else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
  365.         ExecuteStop();
  366.         } else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
  367.         if (m_tStartTime <= m_tStopTime) {
  368.             ExecuteStart();
  369.             ExecuteStop();
  370.         } else {
  371.             ExecuteStop();
  372.             ExecuteStart();
  373.         }
  374.         }
  375.         // always start off flowing when the graph starts streaming
  376.         // unless told otherwise
  377.         m_StreamState = STREAM_FLOWING;
  378.             m_FilterState = new_state;
  379.             break;
  380.  
  381.         case State_Running:
  382.  
  383.             DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
  384.  
  385.             m_tRunStart = tStart;
  386.             // fall-through
  387.  
  388.         default: // case State_Paused:
  389.             m_FilterState = new_state;
  390.     }
  391.     // unblock!
  392.     m_StreamEvent.Set();
  393. }
  394.  
  395.  
  396. void CBaseStreamControl::Flushing(BOOL bInProgress)
  397. {
  398.     CAutoLock lck(&m_CritSec);
  399.     m_bIsFlushing = bInProgress;
  400.     m_StreamEvent.Set();
  401. }
  402.